let Vue

export default class VueRouter {
  constructor (options) {
    this.options = options ?? {}
    this.mode = options.mode ?? 'hash'
    // 路由映射表，path: component
    this._routerMap = Object.create(null)

    //  创建响应式的 currentRouter
    this.currentRouter = Vue.observable({
      path: '/'
    })

    // 初始化 routerMap router-view router-link 路由事件监听
    this.init()
  }

  static install (v) {
    // 确保只安装一次
    if (VueRouter.install?.installed) return

    Vue = v

    Vue.mixin({
      // 将 router 对象绑定到 Vue 原型上，使所有组件都可访问 router 对象
      beforeCreate () {
        if (this.$options.router) {
          // 如果是 Vue 根组件
          this._routerRoot = this
          this._router = this.$options.router
        } else {
          // 如果是子组件
          this._routerRoot = this.$parent._routerRoot
        }

        Object.defineProperty(this, '$router', {
          get () {
            return this._routerRoot._router
          }
        })
        Object.defineProperty(this, '$route', {
          get () {
            return this._routerRoot._router.currentRouter
          }
        })
      }
    })

    VueRouter.install.installed = true
  }

  init () {
    this.initRouterMap()
    this.initComponent()
    this.initEventListener()
  }

  // 将 routes 转化为 { path: component } 的结构
  initRouterMap () {
    const routes = this.options.routes ?? []
    routes.forEach(route => {
      this._routerMap[route.path] = route.component
    })
  }

  initComponent () {
    Vue.component('RouterLink', {
      props: {
        to: String,
        tag: {
          type: String,
          default: 'a'
        }
      },
      methods: {
        jump (e) {
          e.preventDefault()
          this.$router.push(this.to)
          this.$router.currentRouter.path = this.to
        }
      },
      render (h) {
        return h(
          this.tag,
          {
            attrs: {
              href: this.to
            },
            on: {
              click: this.jump
            }
          },
          this.$slots.default
        )
      }
    })

    Vue.component('RouterView', {
      render (h) {
        const component = this.$router._routerMap[this.$route.path]
        return h(component)
      }
    })
  }

  initEventListener () {
    if (this.mode === 'hash') {
      window.addEventListener('hashchange', () => {
        this.currentRouter.path = location.hash.slice(1)
      })
    } else {
      window.addEventListener('popstate', () => {
        this.currentRouter.path = location.pathname
      })
    }
  }

  push (path) {
    if (this.mode === 'hash') {
      location.hash = path
    } else {
      history.pushState('', '', path)
    }
  }

  replace (path) {
    if (this.mode === 'hash') {
      location.hash = path
    } else {
      history.replaceState('', '', path)
    }
  }
}
